/*
 * Copyright 2009 Michal Trzcinka
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 * 
 */

YAHOO.namespace('TRZCINKA.VCARD');

(function() {

  //noinspection JSDeprecatedSymbols
  var Dom = YAHOO.util.Dom,
      Lang = YAHOO.lang,
      DD = YAHOO.util.DD,
      DDProxy = YAHOO.util.DDProxy,
      DDM = YAHOO.util.DragDropMgr,
      DDTarget = YAHOO.util.DDTarget,
      ContextMenu = YAHOO.widget.ContextMenu,
      Menu = YAHOO.widget.Menu,
      Element = YAHOO.util.Element,
      Event = YAHOO.util.Event,
      Constants = YAHOO.TRZCINKA.VCARD.Constants,
      Config = YAHOO.TRZCINKA.VCARD.Config,
      JSON = YAHOO.lang.JSON,
      Tooltip = YAHOO.widget.Tooltip,
      Dialog = YAHOO.widget.Dialog,
      ColorPicker = YAHOO.widget.ColorPicker,
      LOGGER = new YAHOO.widget.LogWriter('view.js'),
      Map = YAHOO.TRZCINKA.VCARD.Map;

  LOGGER.log('Initializing TRZCINKA.VCARD.GridView module...', 'info');

  /**
   * Cells
   */

  YAHOO.TRZCINKA.VCARD.Cells = function() {
    var cells = [];

    this.getCell = function(location) {
      return cells.find(function(cell) {
        return cell.location.equals(location);
      });
    };

    this.putCell = function(cell) {
      cells.push(cell);
    };
  };

  var Cells = YAHOO.TRZCINKA.VCARD.Cells;


  YAHOO.TRZCINKA.VCARD.ItemEditor = function(gridModel) {
    var DIALOG_CONTAINER_ID = Dom.generateId();
    var TEXTAREA_ID = Dom.generateId();

    var dialogContainer = document.createElement('div');
    dialogContainer.id = DIALOG_CONTAINER_ID;
    var body = document.createElement('div');
    Dom.addClass(body, 'bd');
    dialogContainer.appendChild(body);
    var textarea = document.createElement('textarea');
    textarea.id = TEXTAREA_ID;
    body.appendChild(textarea);

    document.body.appendChild(dialogContainer);

    var finishHandler = null;
    var editor = initEditor();
    var dlg = initDialog();
    var insertImageDlg = initInsertImageDlg();

    /* private */
    function initEditor() {
      var editor = new YAHOO.widget.Editor(TEXTAREA_ID, {
        width: '702px',
        height: '200px',
        insert: true,
        toolbar: {
          collapse: false,
          titlebar: 'Editing Item Content',
          draggable: false,
          buttons: [
            {
              group: 'fontstyle',
              label: 'Font Size',
              buttons: [
                {
                  type: 'spin',
                  label: '13',
                  value: 'fontsize',
                  range: [ 9, 75 ],
                  disabled: false
                }
              ]
            },
            {
              type: 'separator'
            },
            {
              group: 'textstyle',
              label: 'Font Style',
              buttons: [
                {
                  type: 'push',
                  label: 'Bold CTRL + SHIFT + B',
                  value: 'bold'
                },
                {
                  type: 'push',
                  label: 'Italic CTRL + SHIFT + I',
                  value: 'italic'
                },
                {
                  type: 'push',
                  label: 'Underline CTRL + SHIFT + U',
                  value: 'underline'
                },
                {
                  type: 'separator'
                },
                {
                  type: 'color',
                  label: 'Font Color',
                  value: 'forecolor',
                  disabled: true
                },
                {
                  type: 'color',
                  label: 'Background Color',
                  value: 'backcolor',
                  disabled: true
                },
                {
                  type: 'push',
                  label: 'Remove Formatting',
                  value: 'removeformat',
                  disabled: true
                },
              ]
            },
            {
              type: 'separator'
            },
            {
              group: 'insertitem',
              label: 'Insert Item',
              buttons: [
                {
                  type: 'push',
                  label: 'HTML Link CTRL + SHIFT + L',
                  value: 'createlink',
                  disabled: true
                },
                {
                  type: 'push',
                  label: 'Insert Image',
                  value: 'insertimage'
                }
              ]
            }
          ]
        }
      });
      editor.on('afterRender', editor.hide);

      editor.on('toolbarLoaded', function() {
        this.toolbar.on('insertimageClick', function() {
          var _sel = this._getSelectedElement();
          if (_sel && _sel.tagName && (_sel.tagName.toLowerCase() == 'img')) {
            //Do the normal thing here..
          } else {
            showInsertImageDialog();
            return false;
          }
        }, this, true);
      }, editor, true);
      editor.on('afterOpenWindow', function() {
        //When the window opens, disable the url of the image so they can't change it
        var url = Dom.get('insertimage_url');
        if (url) {
          url.disabled = true;
        }
      }, editor, true);

      editor.render();

      return editor;
    }

    /* private */
    function initDialog() {
      var dlg = new YAHOO.widget.Dialog(DIALOG_CONTAINER_ID, {
        width:"725px",
        fixedcenter:true,
        modal:true,
        visible:false,
        close: false
      });


      var buttons = [
        {
          text:"Save",
          handler:function() {
            editor.saveHTML();
            finishHandler(editor.get('element').value);
            dlg.hide();
          }
        },
        {
          text:"Cancel",
          handler:function() {
            dlg.hide();
          }
        }
      ];
      dlg.cfg.queueProperty("buttons", buttons);

      dlg.render();

      dlg.showEvent.subscribe(editor.show, editor, true);
      dlg.hideEvent.subscribe(editor.hide, editor, true);

      return dlg;
    }

    /* private */
    function showInsertImageDialog() {
      insertImageDlg.show();
    }

    /* private */
    function initInsertImageDlg() {
      var ID = Dom.generateId();
      var div = document.createElement('div');
      div.id = ID;

      var body = document.createElement('div');
      Dom.addClass(body, 'bd');
      div.appendChild(body);

      var errors = document.createElement('div');
      body.appendChild(errors);

      var form = document.createElement('form');
      form.name = 'image';
      form.method = 'POST';
      form.action = '/images';
      body.appendChild(form);

      var fileInput = document.createElement('input');
      fileInput.type = 'file';
      fileInput.name = 'file';
      form.appendChild(fileInput);

      var gridIdInput = document.createElement('input');
      gridIdInput.type = 'hidden';
      gridIdInput.name = 'grid_id';
      gridIdInput.value = gridModel.getGridId();
      form.appendChild(gridIdInput);

      document.body.appendChild(div);

      var dlg = new YAHOO.widget.Dialog(ID, {
        width:"725px",
        fixedcenter:true,
        modal:true,
        visible:false,
        close: true,
        hideaftersubmit: false
      });

      dlg.beforeShowEvent.subscribe(function() {
        errors.innerHTML = '';
        fileInput.value = '';
      });

      var buttons = [
        {
          text:"Upload",
          handler:dlg.submit
        },
        {
          text:"Cancel",
          handler:dlg.hide
        }
      ];
      dlg.cfg.queueProperty("buttons", buttons);

      dlg.callback.upload = function(o) {
        var response = JSON.parse(o.responseText);
        if (response.url) {
          dlg.hide();
          editor.execCommand('insertimage', response.url);
        } else {
          var text = '';
          response.each(function(error) {
            text += error[0] + ' ' + error[1] + '<br>';
          });
          errors.innerHTML = text;
        }
      };
      dlg.render();
      return dlg;
    }

    /* public */
    this.show = function(content, p_finishHandler) {
      editor.setEditorHTML(content);
      finishHandler = p_finishHandler;
      dlg.show();
    };

  };

  /**
   * Item
   */

  YAHOO.TRZCINKA.VCARD.Item = function(gridModel, cell, data, mode) {

    var that = this;

    if (!YAHOO.TRZCINKA.VCARD.Item._editor) {
      YAHOO.TRZCINKA.VCARD.Item._editor = new YAHOO.TRZCINKA.VCARD.ItemEditor(gridModel);
    }

    var editor = YAHOO.TRZCINKA.VCARD.Item._editor;

    /* private */
    function showEditor() {
      LOGGER.log('entering edit mode in ' + Lang.dump(cell), 'debug');
      var content = that.htmlElement.innerHTML;

      editor.show(content, function(newContent) {
        gridModel.setText(cell.location, newContent);
        that.htmlElement.innerHTML = newContent;
      });
    }

    this.htmlElement = document.createElement('div');
    Dom.addClass(this.htmlElement, 'cell');
    Dom.setStyle(this.htmlElement, 'height', (Config.CELL_HEIGHT * 0.9) + 'px');
    Dom.setStyle(this.htmlElement, 'width', (Config.CELL_WIDTH * 0.9) + 'px');
    Dom.setStyle(this.htmlElement, 'backgroundColor', data.backgroundColor);
    Dom.setStyle(this.htmlElement, 'color', data.foregroundColor);
    this.htmlElement.innerHTML = data.text;

    Event.on(this.htmlElement, 'click', function() {
      showEditor();
    });

    if (mode == 'edit') {
      showEditor();
    }

    //dd
    YAHOO.TRZCINKA.VCARD.DDPlayer = function(id, sGroup, config) {
      YAHOO.TRZCINKA.VCARD.DDPlayer.superclass.constructor.apply(this, arguments);
      this.initConstraints(config.regionId);
      this.initPlayer(id, sGroup, config);
    };

    YAHOO.extend(YAHOO.TRZCINKA.VCARD.DDPlayer, DDProxy, {
      initConstraints: function(regionId) {
        var region = Dom.getRegion(regionId);
        var el = this.getEl();

        Event.onContentReady(el.id, function() {
          var xy = Dom.getXY(el);

          var width = parseInt(Dom.getStyle(el, 'width'), 10);
          var height = parseInt(Dom.getStyle(el, 'height'), 10);

          var left = xy[0] - region.left;
          var right = region.right - xy[0] - width;
          var top = xy[1] - region.top;
          var bottom = region.bottom - xy[1] - height;

          this.setXConstraint(left, right);
          this.setYConstraint(top, bottom);
        }, this, true);

      },

      initPlayer: function(id, sGroup, config) {
        this.startPos = Dom.getXY(this.getEl());
        this.isTarget = false;
        this.startCell = cell;
      },

      onMouseDown: function(e) {
        this.moveAll = e.shiftKey;
      },

      onDragDrop: function(e, id) {
        LOGGER.log('drag drop on ' + id);
        var oldLocation = this.startCell.location;
        var newLocation = id.fromGridLocation();
        if (oldLocation.equals(newLocation)) {
          Dom.setXY(this.getEl(), this.startPos);
        } else {
          if (this.moveAll && gridModel.isStack(oldLocation)) {
            gridModel.moveStack(oldLocation, newLocation);
          } else {
            gridModel.moveItem(oldLocation, newLocation);
          }
          this.startPos = Dom.getXY(this.getEl());
        }
      },

      onInvalidDrop: function() {
        LOGGER.log('invalid drop');
        Dom.setXY(this.getEl(), this.startPos);
      } ,

      endDrag: function() {
      }

    });

    var dd = new YAHOO.TRZCINKA.VCARD.DDPlayer(this.htmlElement, Constants.DD_GROUP, {regionId: Constants.TABLE_ID});

    this.destroy = function() {
      this.htmlElement.parentNode.removeChild(this.htmlElement);
      dd.unreg();
      Event.purgeElement(this.htmlElement, true);
    };

    this.backgroundColorSet = function(color) {
      Dom.setStyle(this.htmlElement, 'background-color', color);
    };

    this.foregroundColorSet = function(color) {
      Dom.setStyle(this.htmlElement, 'color', color);
    };

  };

  var Item = YAHOO.TRZCINKA.VCARD.Item;

  /**
   * Cell
   */

  YAHOO.TRZCINKA.VCARD.Cell = function(location, gridModel) {
    this.location = location;
    var item = null;

    this.htmlElement = document.createElement('td');
    Dom.setStyle(this.htmlElement, 'width', Config.CELL_WIDTH + 'px');
    this.htmlElement.id = location.gridLocationToString();

    var ddTarget = new DDTarget(this.htmlElement.id, Constants.DD_GROUP);

    this.itemCreated = function(data, mode) {
      var stacking = item != null;
      if (stacking) {
        item.destroy();
        item = null;
        this.markAsStack();
      }
      item = new Item(gridModel, this, data, mode);
      this.htmlElement.appendChild(item.htmlElement);
    };

    this.itemRemoved = function() {
      item.destroy();
      item = null;

      var top = gridModel.getTopItem(location);
      if (top) {
        this.itemCreated(top, 'view');
      } else {
        this.unmarkAsStack();
      }
    };

    this.itemMoved = function(targetCell) {
      targetCell.itemCreated(gridModel.getTopItem(targetCell.location), 'view');
      this.itemRemoved();
      if (!gridModel.isStack(location)) {
        this.unmarkAsStack();
      }
    };

    this.stackMoved = function(targetCell) {
      targetCell.itemCreated(gridModel.getTopItem(targetCell.location), 'view');
      item.destroy();
      item = null;
      this.unmarkAsStack();
      targetCell.markAsStack();
    };

    this.backgroundColorSet = function(color) {
      item.backgroundColorSet(color);
    };

    this.foregroundColorSet = function(color) {
      item.foregroundColorSet(color);
    };

    this.topItemSet = function(data) {
      this.itemCreated(data, 'view');
    };

    this.markAsStack = function() {
      this.htmlElement.style.backgroundColor = 'navy';
    };

    this.unmarkAsStack = function() {
      this.htmlElement.style.backgroundColor = 'transparent';
    };

  };

  YAHOO.TRZCINKA.VCARD.Cell.prototype.toString = function() {
    return Lang.dump(this.location);
  };

  var Cell = YAHOO.TRZCINKA.VCARD.Cell;

  /**
   * GridContextMenu
   */

  YAHOO.namespace('TRZCINKA.VCARD.CONTEXT_MENU');
  YAHOO.namespace('TRZCINKA.VCARD.CONTEXT_MENU.ITEMS');

  YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.createItem = function(gridModel, location) {
    return {text: 'Create item', onclick: {fn: function() {
      gridModel.createItem(location);
    }}};
  };

  var CreateItem = YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.createItem;

  YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.removeItem = function(gridModel, location) {
    return {text: 'Remove item', onclick: {fn: function() {
      gridModel.removeItem(location);
    }}};
  };

  var RemoveItem = YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.removeItem;

  YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.setTopItem = function(gridModel, location) {
    var items = gridModel.getItems(location);
    var topItem = gridModel.getTopItem(location);
    var itemdata = items.collect(function(item) {
      return {text: item.text, checked: item == topItem, onclick: {fn: function() {
        gridModel.setTopItem(location, item.order);
      }}};
    });
    return {text: 'Set top item', submenu: {
      id: Dom.generateId(),
      lazyload: true,
      itemdata: itemdata
    }};
  };

  var SetTopItem = YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.setTopItem;

  YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.COLORS = new Map({'Black': 'black', 'White': 'white', 'Red': 'red', 'Blue': 'blue', 'Green':'green', 'Cyan': 'cyan', 'Magenta': 'magenta', 'Yellow': 'yellow'});

  var COLORS = YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.COLORS;

  YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.setOtherColor = function(setColorHandler) {
    return {text: 'Other...', onclick: {fn: function() {

      this.dialog = new Dialog(Dom.generateId(), {
        width : "500px",
        close: false,
        fixedcenter : true,
        visible : true,
        constraintoviewport : true,
        modal: true,
        buttons : [
          {
            text: "OK",
            isDefault:true,
            handler: function() {
              setColorHandler('#' + Dom.get(this.picker.ID.HEX).value);
              this.destroy();
            }
          },
          {
            text: "Cancel",
            handler: function() {
              this.destroy();
            }
          }
        ]
      });

      Dom.addClass(this.dialog.element, 'yui-picker-panel');

      this.dialog.setBody('');
      var el = document.createElement('div');
      el.id = 'yui-picker';
      Dom.addClass(el, 'yui-picker');
      this.dialog.body.appendChild(el);

      // Once the Dialog renders, we want to create our Color Picker
      // instance.
      this.dialog.renderEvent.subscribe(function() {
        if (!this.picker) { //make sure that we haven't already created our Color Picker
          this.picker = new ColorPicker('yui-picker', {
            container: this.dialog,
            images: {
              PICKER_THUMB: "/yui/colorpicker/assets/picker_thumb.png",
              HUE_THUMB: "/yui/colorpicker/assets/hue_thumb.png"
            },
            ids: ColorPicker.prototype.ID
          });
        }
      });

      this.dialog.render(document.body);

    }}};
  };

  var SetOtherColor = YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.setOtherColor;

  YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.setColors = function(gridModel, location) {
    var item = gridModel.getTopItem(location);

    var bgColorMenuItems = [];
    var fgColorMenuItems = [];

    var setBgColor = function(color) {
      gridModel.setBackgroundColor(location, color);
    };
    var setFgColor = function(color) {
      gridModel.setForegroundColor(location, color);
    };

    COLORS.each(function(pair) {
      var color = pair.value;
      var colorName = pair.key;
      bgColorMenuItems.push({text: colorName, checked: item.backgroundColor == color, onclick: {fn: function(event, eventName, color) {
        setBgColor(color);
      }, obj: color}});
      fgColorMenuItems.push({text: colorName,  checked: item.foregroundColor == color, onclick: {fn: function(event, eventName, color) {
        setFgColor(color);
      }, obj: color}});
    });
    bgColorMenuItems.push(new SetOtherColor(setBgColor));
    fgColorMenuItems.push(new SetOtherColor(setFgColor));

    return {
      fg:
      {text: 'Set foreground color', submenu: {
        id: Dom.generateId(),
        lazyload: true,
        itemdata: fgColorMenuItems
      }},
      bg:
      {text: 'Set background color', submenu: {
        id: Dom.generateId(),
        lazyload: true,
        itemdata: bgColorMenuItems
      }}
    };
  };

  var SetColors = YAHOO.TRZCINKA.VCARD.CONTEXT_MENU.ITEMS.setColors;

  YAHOO.TRZCINKA.VCARD.buildContextMenu = function(tableId, gridModel) {

    var ctxMenu = new ContextMenu(Dom.generateId(), {trigger: tableId, lazyload: true});
    ctxMenu.subscribe('beforeShow', function() {
      if (this == ctxMenu) {  // check if the event is fired by the root menu, not a submenu
        this.clearContent();
        var cell;
        if (this.contextEventTarget.tagName == 'TD') {
          cell = this.contextEventTarget;
        } else {
          cell = Dom.getAncestorByTagName(this.contextEventTarget, 'TD');
        }
        var location = cell.id.fromGridLocation();
        if (!gridModel.itemExists(location)) { //add 'create' option to the menu
          this.addItem(new CreateItem(gridModel, location));
        } else { //add 'remove', 'set bg color' and 'set fg color'
          var setColors = new SetColors(gridModel, location);
          this.addItems([new RemoveItem(gridModel, location), setColors.fg, setColors.bg]);
          if (gridModel.isStack(location)) {
            this.addItem(new SetTopItem(gridModel, location));
          }
        }
        this.render();
      }
    });
  };

  var buildContextMenu = YAHOO.TRZCINKA.VCARD.buildContextMenu;


  /**
   * GridView
   */

  YAHOO.TRZCINKA.VCARD.GridView = function(parent, gridModel) {
    LOGGER.log('instantiating GridView', 'debug');

    var cells;

    var eventHandler = function(type, args) {
      var data = args[0][1];
      type = args[0][0];

      LOGGER.log('View received event on model change: type ' + type + ', eventData: ' + JSON.stringify(data), 'debug');

      switch (type) {
        case Constants.MODEL_LOADED_EVENT:

          LOGGER.log('View building grid view because of gridLoaded event received', 'debug');
          cells = new Cells();

          var table = document.createElement('table');
          table.id = Constants.TABLE_ID;
          Dom.addClass(table, 'grid');
          Dom.setStyle(table, 'width', (Config.CELL_WIDTH * data.columns) + 'px');
          Dom.setStyle(table, 'height', (Config.CELL_HEIGHT * data.rows) + 'px');

          for (var y = 0; y < data.rows; y++) {
            var row = document.createElement('tr');
            for (var x = 0; x < data.columns; x++) {
              var cell = new Cell({row: y, column: x}, gridModel);
              row.appendChild(cell.htmlElement);
              cells.putCell(cell);
            }
            Dom.setStyle(row, 'height', Config.CELL_HEIGHT + 'px');
            table.appendChild(row);
          }

          parent.appendChild(table);

          buildContextMenu(table.id, gridModel);

          if (data.items) {

            var locationsItems = new Map();
            data.items.each(function(item) {
              var location = item.location.gridLocationToString();
              if (locationsItems.containsKey(location)) {
                locationsItems.get(location).push(item);
              } else {
                locationsItems.add(location, [item]);
              }
            });

            //noinspection JSUnusedLocalSymbols
            locationsItems.each(function(pair) {
              var items = pair.value;
              items.sort(YAHOO.TRZCINKA.VCARD.MODEL.byOrder);
              items.each(function(item) {
                cell = cells.getCell(pair.key.fromGridLocation());
                cell.itemCreated(item, 'view');
              });

            });
          }

          break;

        case Constants.ITEM_CREATED_EVENT:

          LOGGER.log('View creating item because of itemCreatedEvent event received', 'debug');
          var cell = cells.getCell(data.location);
          cell.itemCreated(data, 'edit');

          break;

        case Constants.ITEM_REMOVED_EVENT:

          LOGGER.log('View removing  item because of itemRemovedEvent event received', 'debug');
          var cell = cells.getCell(data);
          cell.itemRemoved();

          break;

        case Constants.ITEM_MOVED_EVENT:

          LOGGER.log('View moving item because of itemMovedEvent event received', 'debug');
          cells.getCell(data.oldLocation).itemMoved(cells.getCell(data.newLocation), data.order);
          break;

        case Constants.STACK_MOVED_EVENT:

          LOGGER.log('View moving stack because of stackMovedEvent event received', 'debug');
          cells.getCell(data.oldLocation).stackMoved(cells.getCell(data.newLocation));
          break;

        case Constants.BACKGROUND_COLOR_SET_EVENT:

          LOGGER.log('Setting background color because of backgroundColorSetEvent event received', 'debug');
          cells.getCell(data.location).backgroundColorSet(data.color);
          break;

        case Constants.FOREGROUND_COLOR_SET_EVENT:

          LOGGER.log('Setting foreground color because of foregroundColorSetEvent event received', 'debug');
          cells.getCell(data.location).foregroundColorSet(data.color);
          break;

        case Constants.TOP_ITEM_SET_EVENT:

          LOGGER.log('Setting top item because of topItemSetEvent event received', 'debug');
          cells.getCell(data.location).topItemSet(data.item);
          break;


        default:
          LOGGER.log('Unknown event: ' + type, 'error');
      }

    };

    gridModel.gridModelEvent.subscribe(eventHandler);

  };


})();
